﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace consoleTesting
{
    public class NetWsrt3_8
    {

        #region vars from original version

        //+--------------------------------------------------------------------------------------------------------------+
        //| Основные входные параметры. Тейк-профит, стоп-лосс, вывод в безубыток и размер лота.
        //+--------------------------------------------------------------------------------------------------------------+

        string Name = "WallStreet Forex Robot ver. 3.8.5 FINAL (Pirate Edition)";
        string Copy = "Copyright © HELLTEAM^Pirat";
        string Op2 = "Оптимизация для пары";
        string Symbol_Op = "EURUSD m15";
        string Op = "Дата оптимизации";
        //DateTime Date = D'12.05.2011'; //--- Сделал чисто для себя, чтобы видеть от какой даты оптимизация (дата забивается вручную)
        string _TP = "Основные входные параметры";
        //---
        int TakeProfit = 26; //--- (10 2 60)
        int StopLoss = 120; //--- (100 10 200)
        bool UseStopLevels = true; //--- Включение стоповых ордеров. Если выключена, то работают только виртуальные тейки и лоссы.
        bool IsMarketExecution = false; //--- Включение рыночного исполнения открытия ордеров (сначало открывает, затем модифицирует)
        //---
        int SecureProfit = 1; //--- (0 1 5) Вывод в безубыток
        int SecureProfitTriger = 10; //--- (10 2 30)
        int MaxLossPoints = -65; //--- (-200 5 -20) Максимальная просадка для закрытия ордеров Buy и Sell при изменении сигнала (При просадке равной от - MaxLossPoints или меньше (например прибыль 0), ордер закроется)

        string _MM = "Настройка MM";
        //---
        bool RecoveryMode = false; //--- Включение режима восстановления депозита (увеличение лота если случился стоп-лосс)
        double FixedLot = 0.1; //--- Фиксированный объём лота
        double AutoMM = 0.0; //--- ММ включается если AutoMM > 0. Процент риска. При RecoveryMode = false, менять нужно только это значение.
        //--- При AutoMM = 20 и депозите в 1000$, лот будет равен 0,2. Далее лот будет увеличиваться исходя из свободных средств, то есть уже при депозите в 2000$ лот будет равен 0,4.
        double AutoMM_Max = 20.0; //--- Максимальный риск
        int MaxAnalizCount = 50; //--- Число закрытых ранее ордеров для анализа(Используется при RecoveryMode = true)
        double Risk = 25.0; //--- Риск от депозита (Используется при RecoveryMode = true)
        double MultiLotPercent = 1.1; //--- Коэффициент умножение лота (Используется при RecoveryMode = true)

        //+--------------------------------------------------------------------------------------------------------------+
        //| Периоды индикаторов. Кол-во баров для каждого индикатора.
        //+--------------------------------------------------------------------------------------------------------------+

        string _Periods = "Периоды индикаторов";

        //--- Периоды индикаторов (Тоже можно будет заоптить, так как для каждой пары свои)
        int iMA_Period = 75; //--- (60 5 100)
        int iCCI_Period = 18; //--- (10 2 30)
        int iATR_Period = 14; //--- (10 2 30) (!) Можно не оптить
        int iWPR_Period = 11; //--- (10 1 20)

        //+--------------------------------------------------------------------------------------------------------------+
        //| Настройки из DLL
        //+--------------------------------------------------------------------------------------------------------------+
        //| EURUSD     | GBPUSD     | USDCHF     | USDJPY     | USDCAD     |
        //+----------------------------------------------------------------
        //| TP=26;     | TP=50;     | TP=17;     | TP=27;     | TP=14;     |
        //| SL=120;    | SL=120;    | SL=120;    | SL=130;    | SL=150;    |
        //| SP=1;      | SP=2;      | SP=0;      | SP=2;      | SP=2;      |
        //| SPT=10;    | SPT=24;    | SPT=15;    | SPT=14;    | SPT=10;    |
        //| MLP=-65;   | MLP=-200;  | MLP=-40;   | MLP=-80;   | MLP=-30;   |
        //+----------------------------------------------------------------
        //| MA=75;     | MA=75;     | MA=70;     | MA=85;     | MA=65;     |
        //| CCI=18;    | CCI=12;    | CCI=14;    | CCI=12;    | CCI=12;    |
        //| ATR=14;    | ATR=14;    | ATR=14;    | ATR=14;    | ATR=14;    |
        //| WPR=11;    | WPR=12;    | WPR=12;    | WPR=12;    | WPR=16;    |
        //+----------------------------------------------------------------
        //| FATR=6;    | FATR=6;    | FATR=3;    | FATR=0;    | FATR=4;    |
        //| FCCI=150;  | FCCI=290;  | FCCI=170;  | FCCI=2000; | FCCI=130;  |
        //+----------------------------------------------------------------
        //| MAFOA=15;  | MAFOA=12;  | MAFOA=8;   | MAFOA=5;   | MAFOA=5;   |
        //| MAFOB=39;  | MAFOB=33;  | MAFOB=25;  | MAFOB=21;  | MAFOB=15;  |
        //| WPRFOA=-99;| WPRFOA=-99;| WPRFOA=-95;| WPRFOA=-99;| WPRFOA=-99;|
        //| WPRFOB=-95;| WPRFOB=-94;| WPRFOB=-92;| WPRFOB=-95;| WPRFOB=-89;|
        //+----------------------------------------------------------------
        //| MAFC=14;   | MAFC=18;   | MAFC=11;   | MAFC=14;   | MAFC=14;   |
        //| WPRFC=-19; | WPRFC=-19; | WPRFC=-22; | WPRFC=-27; | WPRFC=-6;  |
        //+--------------------------------------------------------------------------------------------------------------+

        //+--------------------------------------------------------------------------------------------------------------+
        //| Параметры оптимизации для правил открытия и закрытия позиции.
        //+--------------------------------------------------------------------------------------------------------------+
        string _Add_Op = "Расширенные параметры оптимизации";
        //---
        string _AddOpenFilters = "---";
        //---
        int FilterATR = 6; //--- (0 1 10) Проверка на вход по ATR для Buy и Sell (if (iATR_Signal <= FilterATR * pp) return (0);) (!) Можно не оптить
        double iCCI_OpenFilter = 150; //--- (100 10 400) Фильтр по iCCI для Buy и Sell. При оптимизации под JPY рекомендуемо оптить по правилу (100 50 4000)
        //---
        string _OpenOrderFilters = "---";
        //---
        int iMA_Filter_Open_a = 15; //--- (4 2 20) Фильтр МА для открытия Buy и Sell (Пунты)
        int iMA_Filter_Open_b = 39; //--- (14 2 50) Фильтр МА для открытия Buy и Sell (Пунты)
        int iWPR_Filter_Open_a = -99; //--- (-100 1 0) Фильтр WPR для открытия Buy и Sell
        int iWPR_Filter_Open_b = -95; //--- (-100 1 0) Фильтр WPR для открытия Buy и Sell
        //---
        string _CloseOrderFilters = "---";
        //---
        int Price_Filter_Close = 14; //--- (10 2 20) Фильтр цены открытия для закрытия Buy и Sell (Пунты)
        int iWPR_Filter_Close = -19; //--- (0 1 -100) Фильтр WPR для закрытия Buy и Sell

        //--- Параметры для автолота
        double MinLot = 0.01;
        double MaxLot = 0.01;
        double LotStep = 0.01;
        int LotValue = 100000;
        double FreeMargin = 1000.0;
        double LotPrice = 1;
        double LotSize;

        //--- Параметры на открытие

        int iWPR_Filter_OpenLong_a;
        int iWPR_Filter_OpenLong_b;
        int iWPR_Filter_OpenShort_a;
        int iWPR_Filter_OpenShort_b;

        //--- Параметры на закрытие

        int iWPR_Filter_CloseLong;
        int iWPR_Filter_CloseShort;

        //+--------------------------------------------------------------------------------------------------------------+
        //| Блок дополнительных переменных
        //+--------------------------------------------------------------------------------------------------------------+
        int Digits = 4;//Количество цифр после десятичной точки в цене текущего инструмента
        int pd = 4;//digits to round
        Symbol EASymbol; //--- Текущий символ
        int MODE_FREEZELEVEL = 2;
        double Point = 0.0001;//Размер пункта текущего инструмента в валюте котировки
        int RandomOpenTimePercent = 0;

        //+--------------------------------------------------------------------------------------------------------------+
        //| Расширенные настройки
        //+--------------------------------------------------------------------------------------------------------------+
        double pp=0.0001;
        double cf = 0.0001;//one point
        bool LongTrade = true; //--- Выключатель длинных позиций
        bool ShortTrade = true; //--- Выключатель коротких позиций
        bool No_Hedge_Trades = true; //--- Одновременное открытие одного Buy и одного Sell ордера. При true не хеджирует.
        bool OneOrderInBarMode = true; //--- При true советник будет открывать только 1 ордер в 1 свече. (В данном случае не более чем 1 раз в 15 минут). В тестере не работает, так как замедляет его в 10 раз.
        int MagicNumber = 777;
        double MaxSpread = 2;
        double OpenSlippage = 2; //--- Проскальзывание для открытия ордера
        double CloseSlippage = 3; //--- Проскальзывание для закрытия ордера
        int RequoteAttempts = 3; //--- Максимальное число повторений при открытии/закрытии ордера при реквотах и других ошибках
        bool WriteLog = true; //--- //--- Включение всплывающих окон в терминале.
        bool WriteDebugLog = true; //--- Включение всплывающих окон об ошибках в терминале.
        bool PrintLogOnChart = true; //--- Включение комментариев на графике (при тестировании выключается автоматически)


        #endregion

        #region my vars to emulate trading

        List<CandleWSRT> Candles;
        decimal oneWeek = 6;

        Symbol Symbol = Symbol.EUR;

        
        public List<Order> openOrders = new List<Order>();
        public List<Order> ordersClosed = new List<Order>();
        public List<Order> closedOrdersToClear = new List<Order>();

        //public int OrdersHistoryTotal//Возвращает количество закрытых позиций и удаленных ордеров в истории текущего счета, загруженной в клиентском терминале. Размер списка истории зависит от текущих настроек вкладки "История счета" терминала. 
        //{
        //    get
        //    {
        //        return ordersClosed.Count;
        //    }
        //}

        public int OrdersTotal//Возвращает общее количество открытых и отложенных ордеров
        {
            get
            {
                return openOrders.Count;
            }
        }

        public CandleWSRT CurrentCandle { get; set; }//Current CandleWSRT to work with

        List<CandleWSRT> M15_Candles = new List<CandleWSRT>();

        
        #endregion


        public NetWsrt3_8(List<CandleWSRT> cndls, List<CandleWSRT> m15candles)
        {
            Candles = cndls;
            EASymbol = Symbol.EUR;
            //--- Параметры на открытие

            iWPR_Filter_OpenLong_a = iWPR_Filter_Open_a;
            iWPR_Filter_OpenLong_b = iWPR_Filter_Open_b;
            iWPR_Filter_OpenShort_a = -100 - iWPR_Filter_Open_a;
            iWPR_Filter_OpenShort_b = -100 - iWPR_Filter_Open_b;

            //--- Параметры на закрытие

            iWPR_Filter_CloseLong = iWPR_Filter_Close;
            iWPR_Filter_CloseShort = -100 - iWPR_Filter_Close;


            M15_Candles = m15candles;

            for (int i = 0; i < cndls.Count; i++)
            {

                CurrentCandle = cndls[i];
                

                CloseOrders(); //--- Сопровождение ордеров
                ModifyOrders(); //--- Вывод в безубыток

                
                //--- Инициализация объёма сдеки
                if (AutoMM > 0.0 && (!RecoveryMode))
                    LotSize = Math.Max(MinLot, Math.Min(MaxLot, Math.Ceiling(Math.Min(AutoMM_Max, AutoMM) / LotPrice / 100.0 * AccountFreeMargin() / LotStep / (LotValue / 100)) * LotStep));
                if (AutoMM > 0.0 && RecoveryMode)
                    LotSize = CalcLots(); //--- Если включен RecoveryMode используем CalcLots
                if (AutoMM == 0.0)
                    LotSize = FixedLot;

                //--- Проверка наличия исторических данных для таймфрейма M15
                //if (M15_Candles.Count < iMA_Period || M15_Candles.Count < iWPR_Period || M15_Candles.Count < iATR_Period || M15_Candles.Count < iCCI_Period)
                

                //---
                if (M15_Candles[0].Time > CurrentCandle.Time)
                    continue;

                Scalper();
                    
            }

            string fileName = String.Format("WSRT3_8/WSRT_Orders_{0}-{1}.xml", Candles[0].Time.ToShortDateString().Replace("/", ""), Candles[Candles.Count - 1].Time.ToShortDateString().Replace("/", ""));
            Stream stream = File.Open(fileName, FileMode.Create);

            System.Xml.Serialization.XmlSerializer xmlSerGaps = new System.Xml.Serialization.XmlSerializer(ordersClosed.GetType());
            xmlSerGaps.Serialize(stream, ordersClosed);

            stream.Close();

            String fileStats = String.Format("WSRT3_8/WSRT_{0}-{1}_Stats.txt", Candles[0].Time.ToShortDateString().Replace("/", ""), Candles[Candles.Count - 1].Time.ToShortDateString().Replace("/", ""));
            StreamWriter sw = File.CreateText(fileStats);

            sw.WriteLine("{0} пунктов", ordersClosed.Sum(x => x.OrderProfit));

            sw.Close();
        }

        private CandleWSRT GetM15CandleForSignal()
        {
            CandleWSRT m15=new CandleWSRT();
            HourPart part=HourPart.None;

            if (CurrentCandle.Time.Minute >= 0 && CurrentCandle.Time.Minute < 15 )
                part=HourPart.One;

            if (CurrentCandle.Time.Minute >= 15 && CurrentCandle.Time.Minute < 30 )
                part=HourPart.Two;

            if (CurrentCandle.Time.Minute >= 30 && CurrentCandle.Time.Minute < 45 )
                part=HourPart.Three;

            if (CurrentCandle.Time.Minute >= 45 && CurrentCandle.Time.Minute <= 59 )
                part=HourPart.Four;

            switch (part)
            { 
                case HourPart.One:
                    m15 = M15_Candles.Find(x=>x.Time==new DateTime(CurrentCandle.Time.Year,CurrentCandle.Time.Month, CurrentCandle.Time.Day,CurrentCandle.Time.Hour,0,0));
                    break;
                case HourPart.Two:
                    m15 = M15_Candles.Find(x=>x.Time==new DateTime(CurrentCandle.Time.Year,CurrentCandle.Time.Month, CurrentCandle.Time.Day,CurrentCandle.Time.Hour,15,0));
                    break;
                case HourPart.Three:
                    m15 = M15_Candles.Find(x=>x.Time==new DateTime(CurrentCandle.Time.Year,CurrentCandle.Time.Month, CurrentCandle.Time.Day,CurrentCandle.Time.Hour,30,0));
                    break;
                case HourPart.Four:
                    m15 = M15_Candles.Find(x=>x.Time==new DateTime(CurrentCandle.Time.Year,CurrentCandle.Time.Month, CurrentCandle.Time.Day,CurrentCandle.Time.Hour,45,0));
                    break;
                default:
                    break;
            }

            return m15;
        }

        private DateTime GetOpenOrderDateTime(Order order)
        {
            DateTime orderOpenDateTime=new DateTime();

            HourPart part=HourPart.None;
            
            if (order.OpenDate.Minute >= 0 && order.OpenDate.Minute < 15 )
                part=HourPart.One;

            if (order.OpenDate.Minute >= 15 && order.OpenDate.Minute < 30 )
                part=HourPart.Two;

            if (order.OpenDate.Minute >= 30 && order.OpenDate.Minute < 45 )
                part=HourPart.Three;

            if (order.OpenDate.Minute >= 45 && order.OpenDate.Minute <= 59 )
                part=HourPart.Four;

            switch (part)
            {
                case HourPart.One:
                    orderOpenDateTime = new DateTime(order.OpenDate.Year, order.OpenDate.Month, order.OpenDate.Day, order.OpenDate.Hour, 0, 0);
                    break;
                case HourPart.Two:
                    orderOpenDateTime = new DateTime(order.OpenDate.Year, order.OpenDate.Month, order.OpenDate.Day, order.OpenDate.Hour, 15, 0);
                    break;
                case HourPart.Three:
                    orderOpenDateTime = new DateTime(order.OpenDate.Year, order.OpenDate.Month, order.OpenDate.Day, order.OpenDate.Hour, 30, 0);
                    break;
                case HourPart.Four:
                    orderOpenDateTime = new DateTime(order.OpenDate.Year, order.OpenDate.Month, order.OpenDate.Day, order.OpenDate.Hour, 45, 0);
                    break;
                default:
                    break;
            }

            return orderOpenDateTime;
        }


        //+--------------------------------------------------------------------------------------------------------------+
        //| Scalper. Основная функция. Сначало производится проверка спреда, далее проверка сигналов на вход.
        //+--------------------------------------------------------------------------------------------------------------+
        void Scalper()
        {
            //+--------------------------------------------------------------------------------------------------------------+

            bool OpenBuyRule = true;
            bool OpenSellRule = true;

            //--- Правило для открытия противоположных ордеров.
            if (No_Hedge_Trades == true && CheckOpenTrade(OrderType.OP_SELL)) OpenBuyRule = false;
            if (No_Hedge_Trades == true && CheckOpenTrade(OrderType.OP_BUY)) OpenSellRule = false;

            //--- Проверка на открытие длинного ордера
            if (OpenLongSignal() && !CheckOpenTrade(OrderType.OP_BUY) && OpenBuyRule && OneOrderInBar(OrderType.OP_BUY) && LongTrade)
            {
                OpenPosition(OrderType.OP_BUY);
                
            }//--- Закрытие if (OpenLongSignal()

            //--- Проверка на открытие короткого ордера   
            if (OpenShortSignal() && !CheckOpenTrade(OrderType.OP_SELL) && OpenSellRule && OneOrderInBar(OrderType.OP_SELL) && ShortTrade)
            {

                OpenPosition(OrderType.OP_SELL);
                
            } //--- Закрытие  if (OpenShortSignal()
        }

        //+--------------------------------------------------------------------------------------------------------------+
        //| OpenLongSignal. Сигнал на открытие длинной позиции.
        //+--------------------------------------------------------------------------------------------------------------+
        bool OpenLongSignal()
        {
            //+--------------------------------------------------------------------------------------------------------------+

            bool result = false;
            bool result1 = false;
            bool result2 = false;
            bool result3 = false;

            CandleWSRT c=GetM15CandleForSignal();
            if(c==null)
                return false;

            //--- Расчет основных сигналов на вход
            double iClose_Signal = (double)c.Close;//iClose(Period.PERIOD_M15, 1);
            double iMA_Signal = c.iMA_Signal;//iMA(Period.PERIOD_M15, iMA_Period, 1);
            double iWPR_Signal = c.iWPR_Signal;//iWPR(Period.PERIOD_M15, iWPR_Period, 1);
            double iATR_Signal = c.iATR_Signal;//iATR(Period.PERIOD_M15, iATR_Period, 1);
            double iCCI_Signal = c.iCCI_Signal;//iCCI(Period.PERIOD_M15, iCCI_Period, 1);

            //---
            double iMA_Filter_a = NormalizeDouble(iMA_Filter_Open_a * pp, pd);
            double iMA_Filter_b = NormalizeDouble(iMA_Filter_Open_b * pp, pd);
            double BidPrice = (double)CurrentCandle.Low; //--- (iClose_Signal >= BidPrice) Сравнение идёт именно с Bid (а не с Ask, как должно быть), так как цена закрытия свечи iClose_Signal формируется на основании значения Bid
            //---

            //--- Сверяем сигнал по АТР с его фильтром
            if (iATR_Signal <= FilterATR * pp) return false;
            //---
            if (iClose_Signal - iMA_Signal > iMA_Filter_a && iClose_Signal - BidPrice >= -cf && iWPR_Filter_OpenLong_a > iWPR_Signal) 
                result1 = true;
            else 
                result1 = false;
            //---
            if (iClose_Signal - iMA_Signal > iMA_Filter_b && iClose_Signal - BidPrice >= -cf && -iCCI_OpenFilter > iCCI_Signal) 
                result2 = true;
            else 
                result2 = false;
            //---
            if (iClose_Signal - iMA_Signal > iMA_Filter_b && iClose_Signal - BidPrice >= -cf && iWPR_Filter_OpenLong_b > iWPR_Signal) 
                result3 = true;
            else 
                result3 = false;
            //---
            if (result1 == true || result2 == true || result3 == true) 
                result = true;
            else 
                result = false;
            //---
            return result;

        }

        //+--------------------------------------------------------------------------------------------------------------+
        //| OpenShortSignal. Сигнал на открытие короткой позиции.
        //+--------------------------------------------------------------------------------------------------------------+
        bool OpenShortSignal()
        {
            //+--------------------------------------------------------------------------------------------------------------+

            bool result = false;
            bool result1 = false;
            bool result2 = false;
            bool result3 = false;

            CandleWSRT c=GetM15CandleForSignal();

            if(c==null)
                return false;

            //--- Расчет основных сигналов на вход
            double iClose_Signal = (double)c.Close;//iClose(Period.PERIOD_M15, 1);
            double iMA_Signal = c.iMA_Signal;//iMA(Period.PERIOD_M15, iMA_Period, 1);//MODE_SMMA=0 - Сглаженное скользящее среднее
            double iWPR_Signal = c.iWPR_Signal;//iWPR(Period.PERIOD_M15, iWPR_Period, 1);
            double iATR_Signal = c.iATR_Signal;//iATR(Period.PERIOD_M15, iATR_Period, 1);
            double iCCI_Signal = c.iCCI_Signal;//iCCI(Period.PERIOD_M15, iCCI_Period, 1);
            //---
            double iMA_Filter_a = NormalizeDouble(iMA_Filter_Open_a * pp, pd);
            double iMA_Filter_b = NormalizeDouble(iMA_Filter_Open_b * pp, pd);
            double BidPrice = (double)CurrentCandle.High;
            //---

            //--- Сверяем сигнал по АТР с его фильтром
            if (iATR_Signal <= FilterATR * pp) return false;
            //---
            if (iMA_Signal - iClose_Signal > iMA_Filter_a && iClose_Signal - BidPrice <= cf && iWPR_Signal > iWPR_Filter_OpenShort_a) 
                result1 = true;
            else 
                result1 = false;
            //---
            if (iMA_Signal - iClose_Signal > iMA_Filter_b && iClose_Signal - BidPrice <= cf && iCCI_Signal > iCCI_OpenFilter) 
                result2 = true;
            else 
                result2 = false;
            //---
            if (iMA_Signal - iClose_Signal > iMA_Filter_b && iClose_Signal - BidPrice <= cf && iWPR_Signal > iWPR_Filter_OpenShort_b) 
                result3 = true;
            else 
                result3 = false;
            //---
            if (result1 == true || result2 == true || result3 == true) 
                result = true;
            else 
                result = false;
            //---
            return result;

        }

        //+--------------------------------------------------------------------------------------------------------------+
        //| OpenPosition. Функция открытия позиции.
        //+--------------------------------------------------------------------------------------------------------------+
        int OpenPosition(OrderType OpType)
        {
            //+--------------------------------------------------------------------------------------------------------------+
            int OpenOrder = 0; //--- Открытие позиции
            int OpenOrderError; //--- Ошибка открытия
            string OpTypeString; //--- Преобразования вида позиции в текстовое значение
            double OpenPrice; //--- Цена открытия
            int lasterror = 0;
            double price = 0;
            //---
            double TP, SL;
            double OrderTP = NormalizeDouble(TakeProfit * pp, pd); //--- Преобразуем тейк-профит в вид Points
            double OrderSL = NormalizeDouble(StopLoss * pp, pd); //--- Преобразуем стоп-лосс в вид Points
            

            double OpenLotSize = LotSize; //--- Расчет объёма позиции

            //--- Если длинная позиция, то
            if (OpType == OrderType.OP_BUY)
            {
                OpenPrice = NormalizeDouble((double)CurrentCandle.Open, Digits);

                //---
                if (UseStopLevels)
                { //--- Если включены стоп-уровни (стоп-лосс и тейк-профит)

                    TP = NormalizeDouble(OpenPrice + OrderTP, Digits); //--- То расчитывает тейк-профит
                    SL = NormalizeDouble(OpenPrice - OrderSL, Digits); //--- и стоп-лосс
                    //---
                }
                else { TP = 0; SL = 0; }

                //--- Если короткая позиция, то   
            }
            else
            {
                OpenPrice = NormalizeDouble((double)CurrentCandle.Open, Digits);

                //---
                if (UseStopLevels)
                {

                    TP = NormalizeDouble(OpenPrice - OrderTP, Digits);
                    SL = NormalizeDouble(OpenPrice + OrderSL, Digits);
                }
                //---
                else { TP = 0; SL = 0; }
            }

            
            OpenOrder = OrderSend(EASymbol, OpType, OpenLotSize, OpenPrice, SL, TP, MagicNumber);
            return OpenOrder;
            
        }

        int orderTicket = 0;
        private int OrderSend(Symbol symbol, OrderType OpType, double LotSize, double price, double SL, double TP, int MagicNumber)
        {
            Order order = new Order();
            order.OpenDate = CurrentCandle.Time;
            order.OrderSymbol = symbol;
            order.OrderType = OpType;
            order.LotSize = LotSize;
            order.OrderOpenPrice = price;
            order.OrderStopLoss = SL;
            order.OrderTakeProfit = TP;
            order.OrderMagicNumber = MagicNumber;
            order.OrderTicket = orderTicket;

            orderTicket++;
            openOrders.Add(order);
            
            return 1;
        }

        //+--------------------------------------------------------------------------------------------------------------+
        //| ModifyOrders. Модификация ордеров в безубыток.
        //+--------------------------------------------------------------------------------------------------------------+
        private void ModifyOrders()
        {
            //+--------------------------------------------------------------------------------------------------------------+

            int total = OrdersTotal - 1;

            Order order;
            //---
            for (int i = total; i >= 0; i--)
            { //--- Счетчик открытых ордеров
               order=openOrders[i];

                    //--- Модификация ордера на покупку
                    if (order.OrderType == OrderType.OP_BUY)
                    {
                        if (order.OrderMagicNumber == MagicNumber && order.OrderSymbol == EASymbol)
                        {
                            if ((double)CurrentCandle.High - order.OrderOpenPrice > SecureProfitTriger * pp && Math.Abs(order.OrderOpenPrice + SecureProfit * pp - order.OrderStopLoss) >= Point)
                            {
                                //--- Модифицируем ордер
                                ModifyOrder(order.OrderTicket, order.OrderOpenPrice, NormalizeDouble(order.OrderOpenPrice + SecureProfit * pp, Digits), order.OrderTakeProfit);
                            }
                        }
                    } //--- Закрытие if (OrderType() == OP_BUY)

                    //--- Модификация ордера на продажу
                    if (order.OrderType == OrderType.OP_SELL)
                    {
                        if (order.OrderMagicNumber == MagicNumber && order.OrderSymbol == EASymbol)
                        {
                            if (order.OrderOpenPrice - (double)CurrentCandle.Low > SecureProfitTriger * pp && Math.Abs(order.OrderOpenPrice - SecureProfit * pp - order.OrderStopLoss) >= Point)
                            {
                                //--- Модифицируем ордер
                                ModifyOrder(order.OrderTicket, order.OrderOpenPrice, NormalizeDouble(order.OrderOpenPrice - SecureProfit * pp, Digits), order.OrderTakeProfit);
                            }
                        }
                    } //--- Закрытие if (OrderType() == OP_SELL)
                }
        } //--- Закрытие for (int i = total - 1; i >= 0; i--)

        //+--------------------------------------------------------------------------------------------------------------+
        //| ModifyOrder. Модификация предварительно выбранного ордера.
        //|  
        //| Параметры:
        //|   sy - наименование инструмента  ("" - текущий символ)
        //|   pp - цена открытия позиции, установки ордера
        //|   sl - ценовой уровень стопа
        //|   tp - ценовой уровень тейка
        //|   cl - цвет
        //+--------------------------------------------------------------------------------------------------------------+
        private void ModifyOrder(int orderTicket, double pp = -1, double sl = 0, double tp = 0)
        {
            //+--------------------------------------------------------------------------------------------------------------+

            Order order = OrderSelect(orderTicket);

            
            //--- Проверка на ошибки переменных 
            if (pp <= 0) 
                pp = order.OrderOpenPrice;
            if (sl < 0) 
                sl = order.OrderStopLoss;
            if (tp < 0) 
                tp = order.OrderTakeProfit;

            //--- Инициализация параметров 
            int dg = Digits;
            pp = NormalizeDouble(pp, dg);
            sl = NormalizeDouble(sl, dg);
            tp = NormalizeDouble(tp, dg);

            //--- Дополнительная проверка на ошибки переменных, после которой
            if (pp != order.OrderOpenPrice || sl != order.OrderStopLoss || tp != order.OrderTakeProfit)
            {
                order.OrderOpenPrice = pp;
                order.OrderStopLoss = sl;
                order.OrderTakeProfit = tp;
            }
        }


        //+--------------------------------------------------------------------------------------------------------------+
        //| ExistPosition. Функция проверки открытых ордеров.
        //| Если открыт ордер возвращает true, если нет, дает разрешение (false, 0) на открытие.
        //+--------------------------------------------------------------------------------------------------------------+
        private int ExistPosition()
        {
            //+--------------------------------------------------------------------------------------------------------------+
            int trade = OrdersTotal - 1;
            Order order;

            for (int i = trade; i >= 0; i--)
            {
                order = openOrders[i];
                
                if (order.OrderMagicNumber == MagicNumber)
                {
                    if (order.OrderSymbol == EASymbol)
                        return 1;
                        //if (order.OrderType <= OrderType.OP_SELL) return 1;

                }
            }
            //---
            return 0;
        }

        private void CloseOrders()
        {
            int total = OrdersTotal - 1;

            bool closeByProfit=false;
            Order order;
            //---
            for (int i = total; i >= 0; i--)
            { //--- Счетчик открытых ордеров
                order = openOrders[i];
                closeByProfit = false;
                //--- Закрытие по профиту или лоссу ордеров на покупку
                if (order.OrderType == OrderType.OP_BUY)
                {
                    if (order.OrderMagicNumber == MagicNumber && order.OrderSymbol == EASymbol)
                    {
                        //if ((double)CurrentCandle.High >= (double)order.OrderOpenPrice + TakeProfit * pp || (double)CurrentCandle.Low <= (double)order.OrderOpenPrice - StopLoss * pp || CloseLongSignal((double)order.OrderOpenPrice, ExistPosition(),out closeByProfit))
                        //{
                            //if ((double)CurrentCandle.High >= (double)order.OrderOpenPrice + TakeProfit * pp)
                            //    CloseOrder(order.OrderTicket, (double)CurrentCandle.High);
                            
                            //if((double)CurrentCandle.Low <= (double)order.OrderOpenPrice - StopLoss * pp)
                            //    CloseOrder(order.OrderTicket, (double)CurrentCandle.Low);
                            if ((double)CurrentCandle.Low <= (double)order.OrderStopLoss)
                                CloseOrder(order.OrderTicket, (double)order.OrderStopLoss);

                            if ((double)CurrentCandle.High >= (double)order.OrderTakeProfit)
                                CloseOrder(order.OrderTicket, (double)order.OrderTakeProfit);


                            if (CloseLongSignal(order,(double)CurrentCandle.Low, ExistPosition(), out closeByProfit) && !closeByProfit)
                                CloseOrder(order.OrderTicket, order.OrderStopLoss);

                            if (CloseLongSignal(order,(double)CurrentCandle.High, ExistPosition(), out closeByProfit) && closeByProfit)
                                CloseOrder(order.OrderTicket, order.OrderStopLoss);

                            
                        //}
                    }
                } //--- Закрытие if (OrderType() == OP_BUY)

                //--- Закрытие по профиту или лоссу ордеров на продажу
                if (order.OrderType == OrderType.OP_SELL)
                {
                    if (order.OrderMagicNumber == MagicNumber && order.OrderSymbol == EASymbol)
                    {
                        //if ((double)CurrentCandle.Low <= order.OrderOpenPrice - TakeProfit * pp || (double)CurrentCandle.High >= order.OrderOpenPrice + StopLoss * pp || CloseShortSignal(order.OrderOpenPrice, ExistPosition(), out closeByProfit))
                        //{
                            //if ((double)CurrentCandle.Low <= order.OrderOpenPrice - TakeProfit * pp)
                            //    CloseOrder(order.OrderTicket, (double)CurrentCandle.Low);
                            
                            //if((double)CurrentCandle.High >= order.OrderOpenPrice + StopLoss * pp)
                            //    CloseOrder(order.OrderTicket, (double)CurrentCandle.High);
                            if ((double)CurrentCandle.High >= order.OrderStopLoss)
                                CloseOrder(order.OrderTicket, (double)order.OrderStopLoss);

                            if ((double)CurrentCandle.Low <= order.OrderTakeProfit)
                                CloseOrder(order.OrderTicket, (double)order.OrderTakeProfit);


                            if (CloseShortSignal(order, (double)CurrentCandle.High, ExistPosition(), out closeByProfit) && !closeByProfit)
                                CloseOrder(order.OrderTicket, order.OrderStopLoss);

                            if (CloseShortSignal(order, (double)CurrentCandle.Low, ExistPosition(), out closeByProfit) && closeByProfit)
                                CloseOrder(order.OrderTicket, order.OrderStopLoss);

                            
                        //}
                    }
                } //--- Закрытие if (OrderType() == OP_SELL)
                
            } //--- Закрытие for (int i = total - 1; i >= 0; i--) {
        }

        //+--------------------------------------------------------------------------------------------------------------+
        //| OneOrderInBar. Функция проверяет, открывался ли ордер внутри нулевой свечи.
        //+--------------------------------------------------------------------------------------------------------------+
        public bool OneOrderInBar(OrderType OpType)
        {
            //+--------------------------------------------------------------------------------------------------------------+

            //--- Если отключена функция, то ничего не расчитываем.
            if (OneOrderInBarMode == false) 
                return true;

            Order order = new Order();
            //--- Делаем выборку по открытым ордерам
            for (int i = openOrders.Count-1; i >= 0; i--)
            {
                order = openOrders[i];
                //---
                if (order.OrderSymbol == EASymbol && order.OrderType == OpType && order.OrderMagicNumber == MagicNumber)
                {

                    //--- Если время закрытия ордера больше времени открытия нулевого бара, то запрещаем открытие нового ордера.
                    if (GetOpenOrderDateTime(order) == GetM15CandleForSignal().Time.Date) 
                        return false;
                }
            }
            //--- Делаем выборку по закрытым ордерам
            for (int i = ordersClosed.Count-1; i >= 0; i--)
            {
                order = ordersClosed[i];
                //---
                if (order.OrderSymbol == EASymbol && order.OrderType == OpType && order.OrderMagicNumber == MagicNumber)
                {

                    //--- Если время закрытия ордера больше времени открытия нулевого бара, то запрещаем открытие нового ордера.
                    if (GetOpenOrderDateTime(order) == GetM15CandleForSignal().Time.Date)
                        return false;
                }
            }

            //---
            return true;
        }

        //+--------------------------------------------------------------------------------------------------------------+
        //| CheckOpenTrade. Функция для проверки открытого ордера. Проверяет был ли открыт ордер по OrderType.
        //| Если был открыт, то возвращает true, если нет, то false.
        //+--------------------------------------------------------------------------------------------------------------+
        public bool CheckOpenTrade(OrderType OpType)
        {
            //+--------------------------------------------------------------------------------------------------------------+

            int total = OrdersTotal;
            Order order = new Order();
            for (int i = total - 1; i >= 0; i--)
            {
                order = openOrders[i];

                if (order.OrderSymbol == Symbol && order.OrderMagicNumber == MagicNumber && order.OrderType == OpType) 
                    return true;
            }
            //---
            return false;
        }

        private double AccountFreeMargin()
        {
            return FreeMargin;
        }

        private Order OrderSelect(int ticket)
        {
            return openOrders.Find(x => x.OrderTicket == ticket);
        }

        //+--------------------------------------------------------------------------------------------------------------+
        //| CloseOrder. Функция закрытия ордера.
        //+--------------------------------------------------------------------------------------------------------------+
        int CloseOrder(int ticket, double price)
        {
            //+--------------------------------------------------------------------------------------------------------------+
            
            //Order order=OrderSelect(ticket);

            //OrderType ordtype = order.OrderType;
            //if (ordtype == OrderType.OP_BUY) 
            //    price = NormalizeDouble(Bid, Digits);
            //if (ordtype == OrderType.OP_SELL) 
            //    price = NormalizeDouble(Ask, Digits);

            //if (Math.Abs(order.OrderTakeProfit - price) <= MODE_FREEZELEVEL * Point) 
            //    return (0); //--- Проверка уровней заморозки тейк-профита
            //if (Math.Abs(order.OrderStopLoss - price) <=  MODE_FREEZELEVEL * Point) 
            //    return (0); //--- Проверка уровней заморозки стоп-лосса

            //if (OrderClose(ticket, price))
            //    return (1); //--- Если ордер закрыт успешно, то возвращаем 1)
            //else
            //    return 0;

            OrderClose(ticket, price);

            return 1;
        }

        private bool OrderClose(int ticket, double price)
        {
            Order order = OrderSelect(ticket);

            if (order.OrderType == OrderType.OP_BUY)
                order.OrderProfit = Math.Round(price - order.OrderOpenPrice, pd);

            if (order.OrderType == OrderType.OP_SELL)
                order.OrderProfit = Math.Round(order.OrderOpenPrice - price, pd);

            order.CloseDate = CurrentCandle.Time;

            ordersClosed.Add(order);
            openOrders.Remove(order);

            return true;
        }

        //+--------------------------------------------------------------------------------------------------------------+
        //| CalcLots. Функция расчета обьема лота.
        //| При AutoMM > 0.0 && RecoveryMode функция CalcLots расчитывает объём лота относительно свободных средств.
        //| 
        //| Также расчет лота производиться исходя из числа открытых в прошлом ордеров. То есть увеличение лота теперь
        //| зависит не только от свободных средств, но и от числа открытых в прошлом советником ордеров.
        //| 
        //| Помимо простого ММ, функция рассчитывает лот исходя из произошедших ранее стоп-лоссов при включенном
        //| параметре RecoveryMode, то есть, при желании можно включить режим восстановления депозита.
        //+--------------------------------------------------------------------------------------------------------------+
        private double CalcLots()
        {
            double SumProfit; //--- Суммарный профит
            int OldOrdersCount; //--- Текущее кол-во закрытых советником ордеров
            double loss; //--- Просадка
            int LossOrdersCount; //--- Число лосей в прошлом
            double pr; //--- Профит
            int ProfitOrdersCount; //--- Кол-во прибыльных ордеров ы прошлом
            double LastPr; //--- Предыдущее значение профит
            int LastCount; //--- Предыдущее значение счетчика ордеров
            double MultiLot = 1; //---  Обнуление значения умножения лота
            //---

            //--- Если ММ включен, то
            if (MultiLotPercent > 0.0 && AutoMM > 0.0)
            {

                //--- Обнуляем значения
                SumProfit = 0;
                OldOrdersCount = 0;
                loss = 0;
                LossOrdersCount = 0;
                pr = 0;
                ProfitOrdersCount = 0;
                //---

                Order ord;
                //--- Выбираем закрытые ранее ордера
                for (int i = ordersClosed.Count - 1; i >= 0; i--)
                {
                    ord = ordersClosed[i];

                    if (ord.OrderSymbol == Symbol && ord.OrderMagicNumber == MagicNumber)
                        {
                            OldOrdersCount++; //--- Считаем ордера
                            SumProfit += ord.OrderProfit; //--- и суммарный профит

                            //--- Если суммарный профит больше pr (для начала больше 0)
                            if (SumProfit > pr)
                            {
                                //--- Инициализируем профит и счетчик прибыльных ордеров
                                pr = SumProfit;
                                ProfitOrdersCount = OldOrdersCount;
                            }
                            //--- Если суммарный профит меньше loss (для начала больше 0)
                            if (SumProfit < loss)
                            {
                                //--- Инициализируем просадку и счетчик убыточных ордеров
                                loss = SumProfit;
                                LossOrdersCount = OldOrdersCount;
                            }
                            //--- Если текущее кол-во подсчитанных ордеров больше или равно MaxAnalizCount (50), то в будущем считаем только свеженькие ордера а старые вычитаем.
                            if (OldOrdersCount >= MaxAnalizCount) break;
                        }
                    
                } //--- Закрытие for (int i = OrdersHistoryTotal() - 1; i >= 0; i--) {


                //--- Если число прибыльных ордеров меньше или равно числу лосей, то расчитываем значение умножения лота MultiLot
                if (ProfitOrdersCount <= LossOrdersCount) 
                    MultiLot = Math.Pow(MultiLotPercent, LossOrdersCount);
                else//--- Если нет, то
                {

                    //--- Инициализируем параметры по профиту
                    SumProfit = pr;
                    OldOrdersCount = ProfitOrdersCount;
                    LastPr = pr;
                    LastCount = ProfitOrdersCount;

                    Order order;
                    //--- Выбираем закрытие ранее ордера (минус число прибыльных ордеров)
                    for (int i = ordersClosed.Count - ProfitOrdersCount - 1; i >= 0; i--)
                    {
                        order = ordersClosed[i];

                        if (order.OrderSymbol == Symbol && order.OrderMagicNumber == MagicNumber)
                        {
                            //--- Если выбрано более 50 ордеров прекразщаем выбирать
                            if (OldOrdersCount >= MaxAnalizCount) break;
                            //---
                            OldOrdersCount++; //--- Считаем кол-во ордеров
                            SumProfit += order.OrderProfit; //--- и профит

                            //--- Если новый профит меньше предыдущего (LastPr), то
                            if (SumProfit < LastPr)
                            {
                                //--- Переинициализируем значения профита и кол-во ордеров
                                LastPr = SumProfit;
                                LastCount = OldOrdersCount;
                            }
                        }
                        
                    } //--- Закрытие for (i = OrdersHistoryTotal() - ProfitOrdersCount - 1; i >= 0; i--) {

                    //--- Если значение счетчика LastCount равно счетчику прибыльных ордеров или прошлый профит равен текщему, то
                    if (LastCount == ProfitOrdersCount || LastPr == pr) 
                        MultiLot = Math.Pow(MultiLotPercent, LossOrdersCount); //--- расчитываем значение умножения лота MultiLot

                    //--- Если нет, то
                    else
                    {
                        //--- Делим положительный (loss - pr) на положительный (LastPr - pr) и сравниваем с риском, после расчитываем умножение лота MultiLot
                        if (Math.Abs(loss - pr) / Math.Abs(LastPr - pr) >= (Risk + 100.0) / 100.0) 
                            MultiLot = Math.Pow(MultiLotPercent, LossOrdersCount);
                        else 
                            MultiLot = Math.Pow(MultiLotPercent, LastCount);
                    }
                }
            } //--- Закрытие if (MultiLotPercent > 0.0 && AutoMM > 0.0) {

            double OpLot = 0;

            //--- Получаем финальный объём лота, исходя из выполненных выше действий
            for (OpLot = Math.Max(MinLot, Math.Min(MaxLot, Math.Ceiling(Math.Min(AutoMM_Max, MultiLot * AutoMM) / 100.0 * AccountFreeMargin() / LotStep / (LotValue / 100)) * LotStep)); OpLot >= 2.0 * MinLot &&
               1.05 * (OpLot * FreeMargin) >= AccountFreeMargin(); OpLot -= MinLot)
            {
            }

            return (OpLot);
        }

        private CandleWSRT GetPrevM1Candle()
        {
            CandleWSRT m1;

            m1 = Candles.Find(x => x.CandleIndex == CurrentCandle.CandleIndex - 1);

            return m1;
        }


        //+--------------------------------------------------------------------------------------------------------------+
        //| CloseLongSignal. Сигнал на закрытие длинной позиции.
        //+--------------------------------------------------------------------------------------------------------------+
        private bool CloseLongSignal(Order order,double Price, int CheckOrders,out bool closeByProfit)
        {
            //+--------------------------------------------------------------------------------------------------------------+

            bool result = false;
            bool result1 = false;
            bool result2 = false;
            //---
            double iWPR_Signal = GetM15CandleForSignal().iWPR_Signal;//iWPR(Period.PERIOD_M15,iWPR_Period, 1);
            double iClose_Signal = (double)GetM15CandleForSignal().Close;//iClose(Period.PERIOD_M15, 1);
            double iOpen_CloseSignal = (double)GetPrevM1Candle().Open;//iOpen(Period.PERIOD_M1, 1);
            double iClose_CloseSignal = (double)GetPrevM1Candle().Close;//iClose(Period.PERIOD_M1, 1);
            //---
            double MaxLoss = NormalizeDouble(MaxLossPoints * pp, pd);
            //---
            double Price_Filter = NormalizeDouble(Price_Filter_Close * pp, pd);
            //double BidPrice = (double)CurrentCandle.Low;
            //---
            closeByProfit = false;
            //---
            if (Price - (double)CurrentCandle.Low <= MaxLoss && iClose_Signal - (double)CurrentCandle.Low <= cf && iWPR_Signal > iWPR_Filter_CloseLong && CheckOrders == 1)
            {
                ModifyOrder(order.OrderTicket, order.OrderOpenPrice, Price, order.OrderTakeProfit);
                result1 = true;
                closeByProfit = false;
            }
            else 
                result1 = false;
            //---
            if (iOpen_CloseSignal > iClose_CloseSignal && (double)CurrentCandle.High - Price >= Price_Filter && CheckOrders == 1)
            {
                ModifyOrder(order.OrderTicket, order.OrderOpenPrice, Price, order.OrderTakeProfit);
                result2 = true;
                closeByProfit = true;
            }
            else 
                result2 = false;
            //---
            if (result1 == true || result2 == true) 
                result = true;
            else 
                result = false;
            //---
            return result;

        }

        //+--------------------------------------------------------------------------------------------------------------+
        //| CloseShortSignal. Сигнал на закрытие короткой позиции.
        //+--------------------------------------------------------------------------------------------------------------+
        private bool CloseShortSignal(Order order, double Price, int CheckOrders, out bool closeByProfit)
        {
            //+--------------------------------------------------------------------------------------------------------------+

            bool result = false;
            bool result1 = false;
            bool result2 = false;
            //---
            double iWPR_Signal = GetM15CandleForSignal().iWPR_Signal;//iWPR(Period.PERIOD_M15, iWPR_Period, 1);
            double iClose_Signal = (double)GetM15CandleForSignal().Close;//iClose(Period.PERIOD_M15, 1);
            double iOpen_CloseSignal = (double)GetPrevM1Candle().Open;//iOpen(Period.PERIOD_M1, 1);
            double iClose_CloseSignal = (double)GetPrevM1Candle().Close;//iClose(Period.PERIOD_M1, 1);
            //---
            double MaxLoss = NormalizeDouble(MaxLossPoints * pp, pd);
            //---
            double Price_Filter = NormalizeDouble(Price_Filter_Close * pp, pd);
            //double BidPrice = Bid;
            //double AskPrice = Ask;
            //---
            closeByProfit = false;
            //---
            if ((double)CurrentCandle.High - Price <= MaxLoss && iClose_Signal - (double)CurrentCandle.High >= -cf && iWPR_Signal < iWPR_Filter_CloseShort && CheckOrders == 1)
            {
                ModifyOrder(order.OrderTicket, order.OrderOpenPrice, Price, order.OrderTakeProfit);
                result1 = true;
                closeByProfit = false;
            }
            else result1 = false;
            //---
            if (iOpen_CloseSignal < iClose_CloseSignal && Price - (double)CurrentCandle.Low >= Price_Filter && CheckOrders == 1)
            {
                ModifyOrder(order.OrderTicket, order.OrderOpenPrice, Price, order.OrderTakeProfit);
                result2 = true;
                closeByProfit = true;
            }
            else 
                result2 = false;
            //---
            if (result1 == true || result2 == true) 
                result = true;
            else 
                result = false;
            //---
            return result;

        }

        private double NormalizeDouble(double value, int digits)//Округление числа с плавающей запятой до указанной точности
        {
            return Math.Round(value, digits);
        }

        

        
        

        





    }

    
}
